#include <stdlib.h>
#include <assert.h>
#include <err.h>

#include "node.h"
#include "edge.h"

struct sNode {
    unsigned int label; /* Número que identifica al nodo */
    int level;          /* Nivel del nodo en el network auxiliar */
    GList *neighbors;   /* Lista de vecinos forward del nodo */
    GList *backwardn;   /* Lista de vecinos backward del nodo */
};

/* Obtiene la arista asociada al nodo 'n' y al nodo identificado con 'label' */
static edge node_getEdge(const node n, const unsigned int label, bool forward);

node node_create(const unsigned int label){
    /* Asigno memoria para alojar la estructura */
    node n = (node) malloc(sizeof(struct sNode));
    if (n == NULL)
        return n;

    /* Inicializo la estructura */
    n->label = label;
    n->level = -1;
    n->neighbors = NULL;
    n->backwardn = NULL;

    assert(n != NULL);
    assert(n->neighbors == NULL);
    assert(n->backwardn == NULL);

    return n;
}

node node_destroy(node n){
    assert (n != NULL);

    /* Libero las listas de vecinos forward y backward */
    g_list_free_full(n->neighbors, (GDestroyNotify) edge_destroy);
    n->neighbors = NULL;
    g_list_free_full(n->backwardn, (GDestroyNotify) edge_destroy);
    n->backwardn = NULL;

    /* Libero la memoria alojada por el nodo */
    free(n);
    n = NULL;

    return n;
}

bool node_addNeighbor(node n1, const node n2, const unsigned int cap){
    edge e = NULL;
    assert(n1 != NULL);
    assert(n2 != NULL);

    /* Se asume que no va a haber aristas repetidas */

    /* Agrego la arista a la lista de vecinos del nodo */
    e = edge_create(n2, cap);
    if(e != NULL){
        n1->neighbors = g_list_prepend(n1->neighbors, e);
        return true;
    }else{
        return false;
    }
}

GList *node_getNeighbors(const node n){
    assert(n != NULL);
    return n->neighbors;
}

GList *node_getBackwardNeighbors(const node n){
    assert(n != NULL);
    return n->backwardn;
}


bool node_addBackwardNeighbor(node n1, const node n2, const unsigned int flow,
                                                    const unsigned int cap){
    edge e = NULL;
    assert(n1 != NULL);
    assert(n2 != NULL);

    /* Agrego la arista backward a la lista de vecinos del nodo */
    e = edge_create(n2, cap);
    edge_increaseFlow(e, flow);
    if (e != NULL){
        n1->backwardn = g_list_prepend(n1->backwardn, e);
        return true;
    }else{
        return false;
    }
}

void node_delBackwardNeighbor(node n1, const node n2){
    edge e = node_getEdge(n1, node_getLabel(n2), false);

    n1->backwardn = g_list_remove(n1->backwardn, e);
}

bool node_neighborExists(const node n, const unsigned int label){
    assert(n != NULL);
    return (node_getEdge(n, label,true) != NULL);
}

static edge node_getEdge(const node n, const unsigned int label, bool forward){
    GList *vecinos = NULL;
    if(forward){
        vecinos = n->neighbors;
    }else{
        vecinos = n->backwardn;
    }
    /* Recorro la lista de vecinos en busca del nodo requerido */
    while(vecinos != NULL){
        if(label == node_getLabel(edge_getNeighbor(vecinos->data))){
            /* Devuelvo la capacidad de la arista */
            return vecinos->data;
        }
        vecinos = vecinos->next;
    }
    return NULL;
}

unsigned int node_getEdgeCap(const node n, const unsigned int label){
    edge e = NULL;

    assert(n != NULL);
    assert(node_neighborExists(n, label));

    e = node_getEdge(n,label,true);

    return edge_getCap(e);
}

unsigned int node_getEdgeFlow(const node n, const unsigned int label){
    edge e = NULL;

    assert(n != NULL);
    assert(node_neighborExists(n, label));

    e = node_getEdge(n,label,true);

    return edge_getFlow(e);
}

unsigned int node_getLabel(const node n){
    assert(n != NULL);

    return n->label;
}

unsigned int *node_getLabelAddress(const node n){
    assert(n != NULL);

    return &(n->label);
}

void node_setLevel(node n, int level){
    assert(n != NULL);

    n->level = level;
}

int node_getLevel(const node n){
    assert(n != NULL);

    return n->level;
}

